/**
 * owsapi
 * http://owsapi.codeplex.com/
 *
 * Copyright (c) 2009 Paul Grenier
 * Licensed under the MIT license (MIT-LICENSE.txt)
 * @name owsapi
 * @fileOverview JavaScript object and methods for accessing SharePoint lists using the WSS RPC Display method.
 * @author Paul Grenier
 * @version 1.0
 * @requires jQuery 1.3.1
 */

/**
 * begin closure for module pattern
 */
(function(){
    /**
     * @name max
     * @description utility function for this module returns the max in an array of numbers
     * @param arr - array of numbers
     * @type number
     */
    Array.max = function(arr){
        return Math.max.apply(Math,arr);
    };
    /**
     * @name min
     * @description utility function for this module returns the min in an array of numbers
     * @param arr - array of numbers
     * @type number
     * @see _debug,
     */
    Array.min = function(arr){
        return Math.min.apply(Math,arr);
    };
    var defaults = {
        ctx: "ctx1",
        baseUrl: "",
        list: "",
        view: "",
        command: "",
        debug: true
    },
    /**
     * @name _debug
     * @description private utility function to log console messages
     * @param msg - string or variable to send to console
     * @param [type] - type of message: log, info, warn, error
     * @return boolean
     */
    _debug = function(msg,type){
        if (window.console && window.console.log && defaults.debug === true){
            if (!type){
                type="log";
            }
            console[type](msg);
            return true;
        }
        return false;
    },
    /**
     * @name isNumber
     * @description utility function for this module returns true if a string can evaluate as a number
     * @param str - string
     * @type boolean
     */
    isNumber = function(str){
        return (/^-?((\d+\.?\d*)|(\.\d+))$/).test(str);
    },
    /**
     * @name ows_url
     * @description utility function for this module returns the URL used by owsapi $.get()
     * @param [o] - object of options
     * @type string (URL)
     * @requires L_Menu_BaseUrl - global variable set by SharePoint master page
     */
    ows_url = function(o){
        o.baseUrl = (!o.baseUrl)?L_Menu_BaseUrl:o.baseUrl;
        o.list = (!o.list)?window[o.ctx].listName:o.list;
        o.view = (!o.view)?window[o.ctx].view:o.view;
        return o.baseUrl+"/_vti_bin/owssvr.dll?XMLDATA=1&List="+
        o.list+"&View="+o.view+o.command;
    },
    /**
     * @name create_ows
     * @description creates the global ows object and its methods
     * @type object
     * @constructor ows
     */
    create_ows = function(){
        ows = {
            /**
             * @name getSchema
             * @description ows method to return all schema nodes of a context
             * @param ctx - string, context
             * @param [rsname] - string, data attribute display name
             * @type object
             * @example ows.getSchema('ctx1');
             */
            getSchema : function(ctx,rsname){
                if(!ows[ctx]){
                    _debug('unknown ctx reference: '+ctx,'error');
                    return false;
                }
                var n = $.grep($(ows[ctx].schema),function(e){
                    if (rsname){
                        return ($(e).attr("rs:name")==rsname);
                    }else{
                        return ($(e).attr("rs:name"));
                    }
                });
                return n;
            },
            /**
             * @name getName
             * @description ows method to return value of the name attribute
             * @param ctx - string, context
             * @param [rsname] - string, data attribute display name
             * @type string or object
             * @example ows.getName('ctx1','% Complete'); //"ows_PercentComplete"
             */
            getName : function(ctx,rsname){
                var n = ows.getSchema(ctx,rsname);
                if (n && n.length > 1){
                    var ns = [];
                    $(n).each(function(i,e){
                        ns.push($(e).attr("name"));
                    });
                    return ns;
                }
                if (n && n.length > 0){
                    return $(n).attr("name");
                }else{
                    _debug('unknown field name: '+rsname,'warn');
                    return false;
                }
            },
            /**
             * @name getType
             * @description ows method to return value of s:datatype attribute
             * @param ctx - string, context
             * @param [rsname] - string, data attribute display name
             * @type string or object
             * @example ows.getType('ctx1','% Complete'); //"float"
             */
            getType : function(ctx,rsname){
                var n = ows.getSchema(ctx,rsname);
                if (n.length > 1){
                    var ns = [];
                    $(n).each(function(i,e){
                        ns.push($(e).find("[nodeName='s:datatype']").attr("dt:type"));
                    });
                    return ns;
                }else{
                    return $(n).find("[nodeName='s:datatype']").attr("dt:type");
                }
            },
            /**
             * @name getNumber
             * @description ows method to return value of rs:number attribute
             * @param ctx - string, context
             * @param rsname - string, data attribute display name
             * @type number
             * @exampel ows.getNumber('ctx1','% Complete'); //"7"
             */
            getNumber : function(ctx,rsname){
                var n = ows.getSchema(ctx,rsname);
                return $(n).attr("rs:number");
            },
            /**
             * @name getPosition
             * @description ows method to return value of rs:number attribute minus 1
             * @param ctx - string
             * @param rsname - string, data attribute display name
             * @type number
             * @example ows.getPosition('ctx1','% Complete'); //6
             */
            getPosition : function(ctx,rsname){
                var p = ows.getNumber(ctx,rsname);
                return parseInt(p,10)-1;
            },
            /**
             * @name getPosition
             * @description ows method to return value of rs:number attribute minus 1
             * @param ctx - string, context
             * @param rsname - string, data attribute display name
             * @type boolean
             * @example ows.isLookup('ctx1','Assigned To'); //true
             */
            isLookup : function(ctx,rsname){
                var n = ows.getSchema(ctx,rsname),
                l = $(n).find("[nodeName='s:datatype']").attr("dt:lookup");
                return l ? true : false;
            },
            /**
             * @name _filter
             * @private
             * @description Internal method scoped to this module filters a data object
             * @param ctx - string, context
             * @param data - object of data nodes
             * @param filter - object of filter parameters
             * @type object
             */
            _filter : function(ctx,data,filter){
                var f = ows.getName(ctx,filter[0]),
                op = filter[1],
                fv = filter[2];
                data = $.grep($(data),function(e){
                    switch (op){
                        case '<':
                            /**
                             * This attribute value is less than filter value
                             */
                            return ($(e).attr(f) < fv);
                        case '>':
                            /**
                             * This attribute value is greater than filter value
                             */
                            return ($(e).attr(f) > fv);
                        case '==':
                            /**
                             * This attribute value is equal to filter value
                             * will compare to the value portion of a lookup or calculated column
                             */
                            if(ows.getType(ctx,filter[0])=='variant'||ows.isLookup(ctx,filter[0])){
                                return ($(e).attr(f) && $(e).attr(f).split(';#')[1] == fv);
                            }
                            return ($(e).attr(f) == fv);
                        case '!=':
                            /**
                             * This attribute value is not equal to filter value
                             * will compare to the value portion of a lookup or calculated column
                             */
                            if(ows.getType(ctx,filter[0])=='variant'||ows.isLookup(ctx,filter[0])){
                                return ($(e).attr(f) && $(e).attr(f).split(';#')[1] != fv);
                            }
                            return ($(e).attr(f) != fv);
                        case '<=':
                            /**
                             * This attribute value is less than or equal to filter value
                             */
                            return ($(e).attr(f) <= fv);
                        case '>=':
                            /**
                             * This attribute value is greater than or equal to filter value
                             */
                            return ($(e).attr(f) >= fv);
                        case '@=':
                            /**
                             * ID of this attribute value is equal to filter value
                             */
                            return ($(e).attr(f) && $(e).attr(f).split(';#')[0] == fv);
                        case '@!=':
                            /**
                             * ID of this attribute value is not equal to filter value
                             */
                            return ($(e).attr(f) && $(e).attr(f).split(';#')[0] != fv);
                        default:
                            _debug('invalid operator found in filter: '+op,'warn');
                            return data;
                    }
                });
                return data;
            },
            /**
             * @name filterRows
             * @description ows method to return filtered set of data
             * @param ctx - string, context
             * @param data - object
             * @param filter - object
             * @type object
             * @example ows.filterRows('ctx1',ows.getData('ctx1'),['% Complete','<',1]);
             * @see getData
             */
            filterRows : function(ctx,data,filter){
                if(!filter||typeof filter != 'object'){
                    _debug('filter is not an object: '+filter,'warn');
                    return data;
                }
                if(filter.length < 3){
                    _debug('too few arguments in filter: '+filter,'warn');
                    return data;
                }
                /**
                 * Check filter object for three or more members
                 * continue to filter data until filter has less than three members
                 */
                while (filter.length > 2){
                    data = ows._filter(ctx,data,filter);
                    filter.splice(0,3);
                }
                return data;
            },
            /**
             * @name getData
             * @description ows method to return all data nodes of a context
             * @param ctx - string, a context
             * @type object
             * @example ows.getData('ctx1');
             */
            getData : function(ctx){
                if(!ows[ctx]){
                    _debug('unknown ctx reference: '+ctx,'error');
                    return false;
                }
                return $(ows[ctx].data);
            },
            /**
             * @name getValues
             * @description ows method to return defined values
             * @param ctx - string, a context
             * @param rsname - string, data attribute display name
             * @param [filter] - object containing at least: rsname, operator, value
             * @type object
             * @example ows.getValues('ctx1','% Complete',['Status','!=','Completed','% Complete','<',1]);
             */
            getValues : function(ctx,rsname,filter){
                var v = [],
                vname = ows.getName(ctx,rsname),
                data = ows.getData(ctx);
                data = filter ? ows.filterRows(ctx,data,filter):data;
                $(data).each(function(i,e){
                    var vx = $(e).attr(vname);
                    if(vx){
                        if(ows.getType(ctx,rsname)=='variant'||ows.isLookup(ctx,rsname)){
                            v.push(vx.split(';#')[1]);
                        }else{
                            v.push(vx);
                        }
                    }
                });
                return v;
            },
            /**
             * @name getId
             * @description ows method to return object of IDs
             * @param ctx - string, a context
             * @param rsname - string, data attribute display name
             * @param [filter] - object containing at least: rsname, operator, value
             * @type object or boolean (false)
             * @example ows.getId('ctx1','Assigned To'); //["27", "1", "27"]
             */
            getId : function(ctx,rsname,filter){
                if(ows.isLookup(ctx,rsname)){
                    var v = [],
                    vname = ows.getName(ctx,rsname),
                    data = ows.getData(ctx);
                    data = filter ? ows.filterRows(ctx,data,filter):data;
                    $(data).each(function(i,e){
                        var vx = $(e).attr(vname);
                        if(vx){
                            v.push(vx.split(';#')[0]);
                        }
                    });
                    return v;
                }else{
                    return false;
                }
            },
            /**
             * @name getUnique
             * @description ows method to return only unique values
             * @param ctx - string, a context
             * @param rsname - string, data attribute display name
             * @param [filter] - object containing at least: rsname, operator, value
             * @type object
             * @example ows.getUniqueId('ctx1','Assigned To');
             */
            getUnique : function(ctx,rsname,filter){
                var b = ows.getValues(ctx,rsname,filter),
                a = [], l = b.length;
                $(b).each(function(i,e){
                    if(a.indexOf(e,0,b)<0){
                        a.push(e);
                    }
                });
                return a;
            },
            /**
             * @name getUniqueId
             * @description ows method to return only unique IDs
             * @param ctx - string, a context
             * @param rsname - string, data attribute display name
             * @param [filter] - object containing at least: rsname, operator, value
             * @type object
             * @example ows.getUniqueId('ctx1','Assigned To'); //["27", "1"]
             */
            getUniqueId : function(ctx,rsname,filter){
                var b = ows.getId(ctx,rsname,filter),
                a = [], l = b.length;
                $(b).each(function(i,e){
                    if(a.indexOf(e,0,b)<0){
                        a.push(e);
                    }
                });
                return a;
            },
            /**
             * @name getAll
             * @description ows method to return all values
             * @param ctx - string, a context
             * @param rsname - string, data attribute display name
             * @param [filter] - object containing at least: rsname, operator, value
             * @type object
             * @example ows.getAll('ctx1','Due Date'); //["2009-05-01 00:00:00", undefined, undefined]
             */
            getAll : function(ctx,rsname,filter){
                var a = [],
                aname = ows.getName(ctx,rsname),
                data = ows.getData(ctx);
                data = filter ? ows.filterRows(ctx,data,filter):data;
                $(data).each(function(i,e){
                    a.push($(e).attr(aname));
                });
                return a;
            },
            /**
             * @name getSum
             * @description ows method to return sum of values
             * @param ctx - string, a context
             * @param rsname - string, data attribute display name
             * @param [filter] - object containing at least: rsname, operator, value
             * @type number
             * @example ows.getSum('ctx1','Actuals'); //12345
             */
            getSum : function(ctx,rsname,filter){
                var svals = ows.getValues(ctx,rsname,filter),
                sum=0;
                $(svals).each(function(i,e){
                    if(e && isNumber(e)){
                        sum += parseFloat(e);
                    }
                });
                return sum;
            },
            /**
             * @name getAvg
             * @description ows method to return average of values
             * @param ctx - string, a context
             * @param rsname - string, data attribute display name
             * @param [filter] - object containing at least: rsname, operator, value
             * @type number
             * example ows.getAvg('ctx1','% Complete'); //0.5333333333333333
             */
            getAvg : function(ctx,rsname,filter){
                var avgsum = ows.getSum(ctx,rsname,filter),
                avgcount = ows.getValues(ctx,rsname,filter).length;
                return avgsum/avgcount;
            },
            /**
             * @name getMax
             * @description ows method to return max of values
             * @param ctx - string, a context
             * @param rsname - string, data attribute display name
             * @param [filter] - object containing at least: rsname, operator, value
             * @type number
             * @example ows.getMax('ctx1','% Complete',['Status','!=','Completed']); //0.5
             */
            getMax : function(ctx,rsname,filter){
                var xvals = ows.getValues(ctx,rsname,filter);
                xvals = $.grep(xvals,function(e){
                    return (e && isNumber(e));
                });
                return Array.max(xvals);
            },
            /**
             * @name getMin
             * @description ows method to return min of values
             * @param ctx - string, a context
             * @param rsname - string, data attribute display name
             * @param [filter] - object containing at least: rsname, operator, value
             * @type number
             * @example ows.getMin('ctx1','% Complete',['Status','!=','Completed']); //0.1
             */
            getMin : function(ctx,rsname,filter){
                var nvals = ows.getValues(ctx,rsname,filter);
                nvals = $.grep(nvals,function(e){
                    return (e && isNumber(e));
                });
                return Array.min(nvals);
            },
            /**
             * @name getAvg
             * @description ows method to return DOM element containing ctx
             * @param ctx - string, a context
             * @type object
             * @example ows.getTable('ctx1');
             */
            getTable : function(ctx){
                var t = $("[id='"+window[ctx].listName+"-"+window[ctx].view+"']");
                return t;
            }
        };
    };
    /**
     * @name owsapi
     * @description function to construct or augment the ows object
     * @param options object overrides defaults
     * @param callback function to execute upon AJAX complete
     * @example owsapi({view:"",command:"&Query=Modified"},mycallback(options));
     */
    window.owsapi = function(options,callback){
        if(!options){
            options = {};
        }
        if(!callback){
            callback = function(){
                return false;
            };
        }
        var o = $.extend(defaults,options);
        if(typeof ows=='undefined'){
            create_ows();
        }
        if(typeof ows[o.ctx]=='object'){
            _debug('object already exists: ows.'+o.ctx,'warn');
        }
        var xhr = $.ajax({
            type: "GET",
            url: ows_url(o),
            async: false,
            success: function(xhr){
                ows[o.ctx] = {
                    schema:$(xhr).find("[nodeName='s:AttributeType']"),
                    data:$(xhr).find("[nodeName='z:row']")
                };
                return callback(o);
            }
        });
    };
})();
/**
 * end closure
 */